package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.uinnova.product.eam.base.local.TaskFromWorkflowContext;
import com.uinnova.product.eam.base.local.TaskFromWorkflowContextValue;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.WorkbenchChargeDone;
import com.uinnova.product.eam.constant.*;
import com.uinnova.product.eam.dto.AssignessInfo;
import com.uinnova.product.eam.dto.SimpPlanInfo;
import com.uinnova.product.eam.feign.workable.FlowableFeign;
import com.uinnova.product.eam.feign.workable.entity.*;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.cj.enums.PlanStatusEnum;
import com.uinnova.product.eam.model.constants.FlowableConstant;
import com.uinnova.product.eam.model.es.*;
import com.uinnova.product.eam.service.*;
import com.uinnova.product.eam.service.cj.service.PlanDesignInstanceService;
import com.uinnova.product.eam.service.es.WorkbenchChargeDoneDao;
import com.uinnova.product.eam.service.exception.BusinessException;
import com.uinnova.product.eam.service.flowable.FlowableApprovalSvc;
import com.uinnova.product.eam.vo.*;
import com.uino.api.client.permission.IRoleApiSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.business.UserInfo;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.util.ESUtil;
import com.uino.service.permission.microservice.IUserSvc;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.client.watcher.GetWatchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Component(value = FlowableConstant.GT_TECHNICAL_SCHEME_APPROVE)
public class ArchReviewApprovalSvcImpl implements FlowableApprovalSvc {

    private static final String ADMIN_TERM_REMARKS = "管理员终止流程";
    @Autowired
    private ArchReviewRotaConfService rotaConfService;
    @Autowired
    private ArchReviewService archReviewService;
    @Autowired
    private ArchReviewExpertConfService expertConfService;
    @Autowired
    private IUserApiSvc userApiSvc;
    @Autowired
    private IRoleApiSvc roleApiSvc;
    @Autowired
    private FlowableFeign flowableFeign;
    @Autowired
    private PlanDesignInstanceService planDesignInstanceService;
    @Autowired
    private ChangeAssigneeRecordService changeAssigneeRecordService;
    @Autowired
    private WorkbenchChargeDoneSvc workbenchChargeDoneSvc;
    @Autowired
    private ArchReviewOrgConfService orgConfService;
    @Autowired
    private IEamNoticeService noticeService;
    @Autowired
    private IUserSvc userSvc;
    @Autowired
    private MeasureFlowRecordService measureFlowRecordService;
    @Autowired
    private MeasureConfService measureConfService;
    @Autowired
    private WorkbenchChargeDoneDao workbenchChargeDoneDao;
    @Resource
    private PlanProcessRelationService planProcessRelationService;
    @Resource
    private CommunicateRecordService communicateRecordService;

    @Override
    public List<String> getApprovalUser(String businessKey, String taskKey) {
        if (StringUtils.isBlank(taskKey)) {
            log.error("taskkey为空");
            return new ArrayList<>();
        }
        //预评审-值班人
        if (FlowableConstant.PRE_REVIEW_OFFICER.equals(taskKey)) {
            return getPreReviewOfficer();
        }
        if (FlowableConstant.PRE_REVIEW_ROTA.equals(taskKey)) {
            return getPreReviewRotas();
        }
        if (FlowableConstant.PMO_REVIEW.equals(taskKey)) {
            return getPMOs();
        }
        return new ArrayList<>();
    }

    @Override
    public List<String> countersignChildUser(String businessKey, String taskKey) {
        return getUsersByRolesDefaultAdmins("预评审-终审");
    }

    private List<String> getPreReviewRotas() {
        return getUsersByRolesDefaultAdmins("预评审-值班复核");
    }

    private List<String> getPMOs() {
        return getUsersByRolesDefaultAdmins("PMO审核");
    }

    private List<String> getUsersByRolesDefaultAdmins(String roleName) {
        List<SysUser> userList = userSvc.getUserByRoleNameAndCount(roleName, 50);
        if (CollectionUtils.isEmpty(userList)) {
            userList = userSvc.getUserByRoleNameAndCount(FlowableConstant.ADMIN_ROLE, 50);
        }
        if (CollectionUtils.isEmpty(userList)) {
            throw new BinaryException("无满足条件审批用户");
        }
        return userList.stream().map(SysUser::getLoginCode).filter(Objects::nonNull).collect(Collectors.toList());
    }

    private List<String> getPreReviewOfficer() {
        //获取当前日期启用的值班人
        List<String> directors = rotaConfService.getDirectorsOnDate(new Date());
        if (!CollectionUtils.isEmpty(directors)) {
            return directors;
        }
        log.error("无值班人，默认返admin角色用户");
        List<SysUser> userList = userSvc.getUserByRoleNameAndCount(FlowableConstant.ADMIN_ROLE, 50);
        if (CollectionUtils.isEmpty(userList)) {
            throw new BinaryException("无满足条件审批用户");
        }
        return userList.stream().map(SysUser::getLoginCode).filter(StringUtils::isNotBlank).collect(Collectors.toList());
    }

    @Override
    public void reject(String businessKey) {
        Long archReviewInfoId = Long.valueOf(businessKey);
        ArchReviewInfo archReviewInfo = archReviewService.getById(archReviewInfoId);
        Assert.notNull(archReviewInfo, "未找到架构评审记录");
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        for (PlanDesignInstance plan : plans) {
            plan.setStatus(PlanStatusEnum.approve_edit.name());
        }
        planDesignInstanceService.saveOrUpdateBatch(plans);
    }

    @Override
    public void childReject(String childBusinessKey) {
        String archReviewInfoId = childBusinessKey.split("_CHILD")[0];
        reject(archReviewInfoId);
    }

    /**
     * 审批不同意
     * @param businessKey
     */
    public void noAgree(String businessKey) {
        Long archReviewInfoId = Long.valueOf(businessKey);
        ArchReviewInfo archReviewInfo = archReviewService.getById(archReviewInfoId);
        Assert.notNull(archReviewInfo, "未找到架构评审记录");
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        for (PlanDesignInstance plan : plans) {
            planDesignInstanceService.gtNoAgreePlan(plan);
        }
        archReviewService.updateArchReviewState(archReviewInfoId, ArchReviewState.NO_PASS);
        //如果关联资产是项目 则更新项目的 架构评审状态 和 架构方案地址
        archReviewService.updateProject(archReviewInfo.getItemType(), archReviewInfo.getProjectCiCode(), plans, ArchReviewState.NO_PASS, archReviewInfo.getProcessInstanceId(), String.valueOf(archReviewInfo.getCreateTime()), String.valueOf(ESUtil.getNumberDateTime()));
    }

    @Override
    public void pass(String businessKey) {
        Long archReviewInfoId = Long.valueOf(businessKey);
        ArchReviewInfo archReviewInfo = archReviewService.getById(archReviewInfoId);
        Assert.notNull(archReviewInfo, "未找到架构评审记录");
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        TaskFromWorkflowContextValue contextValue = TaskFromWorkflowContext.getContext();
        Map<Long, Long> planIdMap = new HashMap<>();
        for (PlanDesignInstance plan : plans) {
            String userLoginCode = plan.getCreatorCode();
            if (contextValue != null && contextValue.getFromWorkflow()
                    && StringUtils.isNotBlank(contextValue.getStartUserLoginCode())) {
                userLoginCode = contextValue.getStartUserLoginCode();
            }
            UserInfo userInfo = userApiSvc.getUserInfoByLoginCode(userLoginCode);
            if (userInfo == null) {
                throw new BusinessException("获取用户信息错误!");
            }
            boolean sendPushlishErrorMsg = false;
            try {
                plan.setProcessInstanceId(archReviewInfo.getProcessInstanceId());
                Long result = planDesignInstanceService.passPlan(plan, userInfo, true);
                if (Objects.equals(result, -2L)) {
                    log.error("方案发布失败:plan:{},msg:{}", JSON.toJSONString(plan), "发布位置被删除");
                    sendPushlishErrorMsg = true;
                } else {
                    planIdMap.put(result, plan.getId());
                }
            } catch (Exception e) {
                log.error("方案发布失败:plan:{},msg:{}", JSON.toJSONString(plan), JSON.toJSONString(e));
                sendPushlishErrorMsg = true;
            }
            if (sendPushlishErrorMsg) {
                noticeService.planPublishFail(plan);
            }
        }
        archReviewService.updateArchReviewState(archReviewInfoId, ArchReviewState.PASS);
        // 保存方案与流程的关系
        planProcessRelationService.saveRelation(archReviewInfo.getProcessInstanceId(), planIdMap);
        //如果关联资产是项目 则更新项目的 架构评审状态 和 架构方案地址
        archReviewService.updateProject(archReviewInfo.getItemType(), archReviewInfo.getProjectCiCode(), plans, ArchReviewState.PASS, archReviewInfo.getProcessInstanceId(), String.valueOf(archReviewInfo.getCreateTime()), String.valueOf(ESUtil.getNumberDateTime()));
    }

    @Override
    public void cancel(String businessKey) {
        Long archReviewInfoId = Long.valueOf(businessKey);
        ArchReviewInfo archReviewInfo = archReviewService.getById(archReviewInfoId);
        Assert.notNull(archReviewInfo, "未找到架构评审记录");
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        for (PlanDesignInstance plan : plans) {
            planDesignInstanceService.gtCancelPlan(plan);
        }
        archReviewService.updateArchReviewState(archReviewInfoId, ArchReviewState.CANCEL);
        //如果关联资产是项目 则更新项目的 架构评审状态 和 架构方案地址
        archReviewService.updateProject(archReviewInfo.getItemType(), archReviewInfo.getProjectCiCode(), plans, ArchReviewState.CANCEL, archReviewInfo.getProcessInstanceId(), String.valueOf(archReviewInfo.getCreateTime()), String.valueOf(ESUtil.getNumberDateTime()));
        //所有待办关闭
        workbenchChargeDoneSvc.batchModifyWorkbenchTaskDone(archReviewInfo.getProcessInstanceId(), archReviewInfo.getId().toString());
    }

    /**
     * 执行审批
     * @param taskRequest
     * @param task
     * @return
     */
    public TaskResponse completeTask(TaskRequest taskRequest, TaskResponse task) {
        Long archReviewInfoId = Long.valueOf(task.getBusinessKey());
        ///预评审-值班人评审节点：维护处理人
        if (FlowableConstant.PRE_REVIEW_OFFICER.equals(task.getDescription())) {
            archReviewService.updatePreReviewOfficer(archReviewInfoId, SysUtil.getCurrentUserInfo().getLoginCode());
        }

        //任务本地变量维护流程批次（驳回至初始节点算新的批次）,方便查任务时返回对应的度量记录
        setFlowBatchInTaskInfo(archReviewInfoId, taskRequest);

        //PMO驳回至初始节点时，维护流程度量记录
        if (FLOWACTION.REJECT_INIT_OWNER.equals(taskRequest.getAction())) {
            MeasureFlowRecordSaveVo saveVo = new MeasureFlowRecordSaveVo();
            saveVo.setArchReviewId(archReviewInfoId);
            saveVo.setAction(2);
            measureFlowRecordService.save(saveVo);
        }

        if (!FLOWACTION.ACCETP.equals(taskRequest.getAction())) {
            return flowableFeign.completeTask(taskRequest);
        }
        //预评审-值班人评审 or 预评审-值班复核 or 预评审-终审
        if (FlowableConstant.PRE_REVIEW_OFFICER.equals(task.getDescription())
                || FlowableConstant.PRE_REVIEW_ROTA.equals(task.getDescription())
                || FlowableConstant.PRE_REVIEW_FINAL_EXPERT.equals(task.getDescription())) {
            if (StringUtils.isBlank(taskRequest.getFeatureReviewType())) {
                throw new BusinessException("后续评审方式不能为空");
            }
            if (!FeatureReviewType.listString().contains(taskRequest.getFeatureReviewType())) {
                throw new BusinessException("不支持的后续评审方式");
            }
            FeatureReviewType featureReviewType = FeatureReviewType.valueOf(taskRequest.getFeatureReviewType());
            String stage = task.getDescription();
            //更新架构评审信息的后续评审方式
            archReviewService.updateFeatureReviewType(archReviewInfoId, featureReviewType, stage);
            //预评审-值班人评审节点：更新受理日期
            if (FlowableConstant.PRE_REVIEW_OFFICER.equals(task.getDescription())) {
                archReviewService.updateAcceptTimeIfEmpty(archReviewInfoId);
            }
            //非预评审-值班复核，逻辑结束
            if (!FlowableConstant.PRE_REVIEW_ROTA.equals(task.getDescription())) {
                return flowableFeign.completeTask(taskRequest);
            }
            //预评审-值班复核节点：根据后续评审方式选择下一节点
            //1.后续评审方式：预评审-专家评审
            if (FeatureReviewType.EXPERT.equals(featureReviewType)) {
                List<GroupDomainExpert> domainExperts = expertConfService.randomGroupABByDomain();
                List<ChildProcessRequest> childProcessRequests = new ArrayList<>();
                int index = 0;
                for (GroupDomainExpert expert : domainExperts) {
                    ChildProcessRequest childProcessRequest = new ChildProcessRequest();
                    childProcessRequest.setChildBusinessKey(task.getBusinessKey() + "_CHILD_EXPERT_SUB_" + index);
                    Map<String, Object> stringObjectHashMap = new HashMap<>();
                    stringObjectHashMap.put("assigneeList", expert.getExperts());
                    stringObjectHashMap.put("group", expert.getGroup());
                    stringObjectHashMap.put("domainId", expert.getDomainId());
                    stringObjectHashMap.put("domainName", expert.getDomainName());
                    childProcessRequest.setChildVariable(stringObjectHashMap);
                    childProcessRequests.add(childProcessRequest);
                    index++;
                }
                HashMap<String, Object> stringObjectHashMap = new HashMap<>();
                stringObjectHashMap.put("aprvNode", "expert");
                stringObjectHashMap.put("activeChildProcess", "true");

                taskRequest.setRouterVariables(stringObjectHashMap);
                taskRequest.setChildProcessRequestList(childProcessRequests);

                //度量测评版本
                MeasureConfInfo measureConfInfo = measureConfService.latestInfo();
                MeasureFlowRecordSaveVo saveVo = new MeasureFlowRecordSaveVo();
                saveVo.setArchReviewId(archReviewInfoId);
                saveVo.setAction(3);
                saveVo.setMeasureConfId(-1L);
                if (measureConfInfo != null && measureConfInfo.getStatus() == 1) {
                    saveVo.setMeasureConfId(measureConfInfo.getId());
                }
                measureFlowRecordService.save(saveVo);
                return flowableFeign.completeTask(taskRequest);
            }
            //2.后续评审方式：PMO审核
            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("aprvNode", "pmo");
            taskRequest.setRouterVariables(stringObjectHashMap);

            //度量测评版本
            MeasureFlowRecordSaveVo saveVo = new MeasureFlowRecordSaveVo();
            saveVo.setArchReviewId(archReviewInfoId);
            saveVo.setAction(3);
            saveVo.setMeasureConfId(-1L);
            measureFlowRecordService.save(saveVo);
            return flowableFeign.completeTask(taskRequest);
        }
        return flowableFeign.completeTask(taskRequest);
    }

    private List<ArchReviewStage> rejectToSubmitterNodes() {
        return Arrays.asList(ArchReviewStage.ORG_COUNTER_SIGN_REJECT,
                ArchReviewStage.ORG_OR_SIGN_REJECT,
                ArchReviewStage.PRE_REVIEW_OFFICER_REJECT,
                ArchReviewStage.PRE_REVIEW_ROTA_REJECT,
                ArchReviewStage.PRE_REVIEW_EXPERT_REJECT,
                ArchReviewStage.PRE_REVIEW_FINAL_EXPERT_REJECT,
                ArchReviewStage.PMO_REVIEW_REJCET,
                ArchReviewStage.START_USER_SUBMIT);
    }

    /**
     * 当前流程节点
     * @param taskId
     * @return
     */
    public ArchReviewStage gtFlowStageByTaskId(String taskId) {
        TaskResponse taskResponse = flowableFeign.getTaskInfoByTaskId(taskId);
        if (!FlowableConstant.GT_TECHNICAL_SCHEME_APPROVE.equals(taskResponse.getProcessDefinitionKey())
                || StringUtils.isBlank(taskResponse.getDescription())) {
            return null;
        }
        return gtFlowStage(taskResponse.getDescription());
    }

    private ArchReviewStage gtFlowStage(String description) {
        switch (description) {
            case FlowableConstant.START_USER_SUBMIT:
                return ArchReviewStage.START_USER_SUBMIT;
            case FlowableConstant.ORG_COUNTER_SIGN:
                return ArchReviewStage.ORG_COUNTER_SIGN;
            case FlowableConstant.ORG_COUNTER_SIGN_REJECT:
                return ArchReviewStage.ORG_COUNTER_SIGN_REJECT;
            case FlowableConstant.ORG_OR_SIGN:
                return ArchReviewStage.ORG_OR_SIGN;
            case FlowableConstant.ORG_OR_SIGN_REJECT:
                return ArchReviewStage.ORG_OR_SIGN_REJECT;
            case FlowableConstant.PRE_REVIEW_OFFICER:
                return ArchReviewStage.PRE_REVIEW_OFFICER;
            case FlowableConstant.PRE_REVIEW_OFFICER_REJECT:
                return ArchReviewStage.PRE_REVIEW_OFFICER_REJECT;
            case FlowableConstant.PRE_REVIEW_ROTA:
                return ArchReviewStage.PRE_REVIEW_ROTA;
            case FlowableConstant.PRE_REVIEW_ROTA_REJECT:
                return ArchReviewStage.PRE_REVIEW_ROTA_REJECT;
            case FlowableConstant.PRE_REVIEW_EXPERT:
                return ArchReviewStage.PRE_REVIEW_EXPERT;
            case FlowableConstant.PRE_REVIEW_EXPERT_REJECT:
                return ArchReviewStage.PRE_REVIEW_EXPERT_REJECT;
            case FlowableConstant.PRE_REVIEW_FINAL_EXPERT:
                return ArchReviewStage.PRE_REVIEW_FINAL_EXPERT;
            case FlowableConstant.PRE_REVIEW_FINAL_EXPERT_REJECT:
                return ArchReviewStage.PRE_REVIEW_FINAL_EXPERT_REJECT;
            case FlowableConstant.PMO_REVIEW:
                return ArchReviewStage.PMO_REVIEW;
            case FlowableConstant.PMO_REVIEW_REJCET:
                return ArchReviewStage.PMO_REVIEW_REJCET;
            default:
                throw new BusinessException("暂未定义流程阶段：{}", description);
        }
    }

//    /**
//     * 审批记录
//     * @param processInstanceId
//     * @return
//     */
//    public GTApproveRecord archReviewApproveRecords(String processInstanceId) {
//        GTApproveRecord archReviewApproveRecord = new GTApproveRecord();
//        List<HistoryTaskResponse> tasks = new ArrayList<>();
//        List<HistoryTaskResponse> histories = flowableFeign.getHistoryTaskByCurrentProcessInstanceId(processInstanceId);
//        List<TaskResponse> actives = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
//        if (!BinaryUtils.isEmpty(histories)){
//            List<HistoryTaskResponse> historyTaskResponses = histories.stream().sorted(Comparator.comparing(HistoryTaskResponse::getCommitTime)).collect(Collectors.toList());
//            extracted(historyTaskResponses);
//            tasks.addAll(historyTaskResponses);
//        }
//        if (!CollectionUtils.isEmpty(actives)) {
//            Set<String> assignees = new HashSet<>();
//            Set<String> currentAssigneeLoginCodes = actives.stream().map(TaskResponse::getUserId).collect(Collectors.toSet());
//            CSysUser cSysUser = new CSysUser();
//            cSysUser.setLoginCodes(currentAssigneeLoginCodes.toArray(new String[currentAssigneeLoginCodes.size()]));
//            List<SysUser> sysUserByCdt = userApiSvc.getSysUserByCdt(cSysUser);
//            Map<String, SysUser> sysUserMap = sysUserByCdt.parallelStream()
//                    .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));
//            for (TaskResponse taskResponse : actives) {
//                HistoryTaskResponse historyTaskResponse = new HistoryTaskResponse();
//                historyTaskResponse.setTaskId(taskResponse.getTaskId());
//                historyTaskResponse.setTaskName(taskResponse.getTaskName());
//                historyTaskResponse.setUserId(taskResponse.getUserId());
//                historyTaskResponse.setProcessInstanceName(taskResponse.getProcessInstanceName());
//                Map<String, Object> variables = new ConcurrentHashMap<>();
//                variables.put("pass", "todo");
//                variables.put("user", sysUserMap.containsKey(taskResponse.getUserId()) ? sysUserMap.get(taskResponse.getUserId()).getUserName() : taskResponse.getUserId());
//                variables.put("icon", sysUserMap.containsKey(taskResponse.getUserId()) ? sysUserMap.get(taskResponse.getUserId()).getIcon() : "");
//                historyTaskResponse.setVariables(variables);
//                assignees.add(sysUserMap.containsKey(taskResponse.getUserId()) ? sysUserMap.get(taskResponse.getUserId()).getUserName() : taskResponse.getUserId());
//                tasks.add(historyTaskResponse);
//            }
//            if (!CollectionUtils.isEmpty(assignees)) {
//                archReviewApproveRecord.setCurrentAssignees(assignees.stream().collect(Collectors.joining(",")));
//            }
//        }
//        //维护转办记录
//        List<ChangeAssigneeRecord> changeAssigneeRecords = changeAssigneeRecordService.queryByProcessInstanceId(processInstanceId);
//        List<HistoryTaskResponse> results = new ArrayList<>();
//        if (!CollectionUtils.isEmpty(changeAssigneeRecords)) {
//            Set<String> orignAssignees = changeAssigneeRecords.stream().map(ChangeAssigneeRecord::getOrignAssignee).collect(Collectors.toSet());
//            CSysUser cSysUser = new CSysUser();
//            cSysUser.setLoginCodes(orignAssignees.toArray(new String[orignAssignees.size()]));
//            List<SysUser> sysUserByCdt = userApiSvc.getSysUserByCdt(cSysUser);
//            Map<String, SysUser> sysUserMap = sysUserByCdt.parallelStream()
//                    .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));
//            Map<String, List<ChangeAssigneeRecord>> groupByTaskId = changeAssigneeRecords.stream().collect(Collectors.groupingBy(ChangeAssigneeRecord::getTaskId));
//            for (HistoryTaskResponse task : tasks) {
//                String taskId = task.getTaskId();
//                if (!groupByTaskId.containsKey(taskId)) {
//                    results.add(task);
//                    continue;
//                }
//                List<ChangeAssigneeRecord> records = groupByTaskId.get(taskId);
//                for (ChangeAssigneeRecord record : records) {
//                    HistoryTaskResponse r = new HistoryTaskResponse();
//                    r.setProcessInstanceName(task.getProcessInstanceName());
//                    r.setTaskName(task.getTaskName());
//                    r.setUserId(record.getOrignAssignee());
//                    r.setTaskId(task.getTaskId());
//                    Map<String, Object> variables = new ConcurrentHashMap<>();
//                    variables.put("pass", "autoCLose");
//                    variables.put("user", sysUserMap.containsKey(record.getOrignAssignee()) ? sysUserMap.get(record.getOrignAssignee()).getUserName() : record.getOrignAssignee());
//                    variables.put("icon", sysUserMap.containsKey(record.getOrignAssignee()) ? sysUserMap.get(record.getOrignAssignee()).getIcon() : "");
//                    variables.put("remarks", "管理员转办");
//                    r.setVariables(variables);
//                    results.add(r);
//                }
//                results.add(task);
//            }
//        } else {
//            results = EamUtil.copy(tasks, HistoryTaskResponse.class);
//        }
//        archReviewApproveRecord.setTasks(results);
//        return archReviewApproveRecord;
//    }

    /**
     * 审批记录
     * @param processInstanceId
     * @return
     */
    public GTApproveRecord archReviewApproveRecords(String processInstanceId) {
        GTApproveRecord archReviewApproveRecord = new GTApproveRecord();
        List<HistoryTaskResponse> tasks = new ArrayList<>();
        List<HistoryTaskResponse> histories = flowableFeign.getHistoryTaskByCurrentProcessInstanceId(processInstanceId);
        List<TaskResponse> actives = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
        if (!BinaryUtils.isEmpty(histories)){
            List<HistoryTaskResponse> historyTaskResponses = histories.stream().sorted(Comparator.comparing(HistoryTaskResponse::getCommitTime)).collect(Collectors.toList());
            extracted(historyTaskResponses);
            tasks.addAll(historyTaskResponses);
        }
        if (!CollectionUtils.isEmpty(actives)) {
            Set<String> assignees = new HashSet<>();
            Set<String> currentAssigneeLoginCodes = actives.stream().map(TaskResponse::getUserId).collect(Collectors.toSet());
            CSysUser cSysUser = new CSysUser();
            cSysUser.setLoginCodes(currentAssigneeLoginCodes.toArray(new String[currentAssigneeLoginCodes.size()]));
            List<SysUser> sysUserByCdt = userApiSvc.getSysUserByCdt(cSysUser);
            Map<String, SysUser> sysUserMap = sysUserByCdt.parallelStream()
                    .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));
            for (TaskResponse taskResponse : actives) {
                HistoryTaskResponse historyTaskResponse = new HistoryTaskResponse();
                historyTaskResponse.setTaskId(taskResponse.getTaskId());
                historyTaskResponse.setTaskName(taskResponse.getTaskName());
                historyTaskResponse.setUserId(taskResponse.getUserId());
                historyTaskResponse.setProcessInstanceName(taskResponse.getProcessInstanceName());
                historyTaskResponse.setDescription(taskResponse.getDescription());
                Map<String, Object> variables = taskResponse.getTaskEchoVariables();
                if (variables == null) {
                    variables = new HashMap<>();
                }
                variables.put("pass", "todo");
                variables.put("user", sysUserMap.containsKey(taskResponse.getUserId()) ? sysUserMap.get(taskResponse.getUserId()).getUserName() : taskResponse.getUserId());
                variables.put("icon", sysUserMap.containsKey(taskResponse.getUserId()) ? sysUserMap.get(taskResponse.getUserId()).getIcon() : "");
                historyTaskResponse.setVariables(variables);
                assignees.add(sysUserMap.containsKey(taskResponse.getUserId()) ? sysUserMap.get(taskResponse.getUserId()).getUserName() : taskResponse.getUserId());
                tasks.add(historyTaskResponse);
            }
            if (!CollectionUtils.isEmpty(assignees)) {
                archReviewApproveRecord.setCurrentAssignees(assignees.stream().collect(Collectors.joining(",")));
            }
        }
        //维护转办记录
        List<ChangeAssigneeRecord> changeAssigneeRecords = changeAssigneeRecordService.queryByProcessInstanceId(processInstanceId);
        List<HistoryTaskResponse> olds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(changeAssigneeRecords)) {
            Set<String> orignAssignees = changeAssigneeRecords.stream().map(ChangeAssigneeRecord::getOrignAssignee).collect(Collectors.toSet());
            CSysUser cSysUser = new CSysUser();
            cSysUser.setLoginCodes(orignAssignees.toArray(new String[orignAssignees.size()]));
            List<SysUser> sysUserByCdt = userApiSvc.getSysUserByCdt(cSysUser);
            Map<String, SysUser> sysUserMap = sysUserByCdt.parallelStream()
                    .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));
            Map<String, List<ChangeAssigneeRecord>> groupByTaskId = changeAssigneeRecords.stream().collect(Collectors.groupingBy(ChangeAssigneeRecord::getTaskId));
            for (HistoryTaskResponse task : tasks) {
                String taskId = task.getTaskId();
                if (!groupByTaskId.containsKey(taskId)) {
                    olds.add(task);
                    continue;
                }
                List<ChangeAssigneeRecord> records = groupByTaskId.get(taskId);
                for (ChangeAssigneeRecord record : records) {
                    HistoryTaskResponse r = new HistoryTaskResponse();
                    r.setProcessInstanceName(task.getProcessInstanceName());
                    r.setTaskName(task.getTaskName());
                    r.setUserId(record.getOrignAssignee());
                    r.setTaskId(task.getTaskId());
                    Map<String, Object> variables = new ConcurrentHashMap<>();
                    variables.put("pass", "autoCLose");
                    variables.put("user", sysUserMap.containsKey(record.getOrignAssignee()) ? sysUserMap.get(record.getOrignAssignee()).getUserName() : record.getOrignAssignee());
                    variables.put("icon", sysUserMap.containsKey(record.getOrignAssignee()) ? sysUserMap.get(record.getOrignAssignee()).getIcon() : "");
                    variables.put("remarks", "管理员转办");
                    r.setVariables(variables);
                    olds.add(r);
                }
                olds.add(task);
            }
        } else {
            olds = EamUtil.copy(tasks, HistoryTaskResponse.class);
        }
        archReviewApproveRecord.setTasks(olds);

        if (CollectionUtils.isEmpty(tasks)) {
            return archReviewApproveRecord;
        }
        List<GTApproveRecordTask> results = newTasks(tasks);
        archReviewApproveRecord.setNewTasks(results);
        return archReviewApproveRecord;
    }

    /**
     * 审批记录
     * @param processInstanceId
     * @return
     */
    public GTApproveRecord archReviewApproveRecordsNew(String processInstanceId) {
        GTApproveRecord approveRecord = new GTApproveRecord();
        List<HistoryTaskResponse> historyTasks = new ArrayList<>();
        List<HistoryTaskResponse> histories = flowableFeign.getHistoryTaskByCurrentProcessInstanceId(processInstanceId);
        List<TaskResponse> actives = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
        List<GTApproveRecordTask> newTasks = new ArrayList<>();
        List<HistoryTaskResponse> oldTasks = new ArrayList<>();
        // 处理历史任务 提交-驳回-撤回等
        if (!BinaryUtils.isEmpty(histories)) {
            histories = histories.stream()
                    .filter(h -> !h.getVariables().get("pass").equals("autoCLose"))
                    .sorted(Comparator.comparing(HistoryTaskResponse::getCommitTime))
                    .collect(Collectors.toList());
            extracted(histories);
            historyTasks.addAll(histories);
        }
        // 新类型 历史数据
        List<GTApproveRecordTask> newHistoryTask = newTasks(historyTasks);
        //当前任务
        List<HistoryTaskResponse> currentTasks = new ArrayList<>();
        // 处理当前任务并转换为历史任务响应
        Set<String> assignees = new HashSet<>();
        if (!org.apache.commons.collections4.CollectionUtils.isEmpty(actives)) {
            Set<String> currentAssigneeLoginCodes = actives.stream()
                    .map(TaskResponse::getUserId)
                    .collect(Collectors.toSet());
            CSysUser cSysUser = new CSysUser();
            cSysUser.setLoginCodes(currentAssigneeLoginCodes.toArray(new String[0]));
            List<SysUser> sysUsers = userApiSvc.getSysUserByCdt(cSysUser);
            Map<String, SysUser> userMap = sysUsers.stream()
                    .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));

            for (TaskResponse active : actives) {
                HistoryTaskResponse response = new HistoryTaskResponse();
                response.setTaskId(active.getTaskId());
                response.setTaskName(active.getTaskName());
                response.setUserId(active.getUserId());
                response.setCommitTime(active.getTaskCreateTime());
                response.setProcessInstanceName(active.getProcessInstanceName());
                response.setDescription(active.getDescription());
                Map<String, Object> variables = active.getTaskEchoVariables();
                variables.put("pass", "todo");
                SysUser user = userMap.get(active.getUserId());
                variables.put("user", user != null ? user.getUserName() : active.getUserId());
                variables.put("icon", user != null ? user.getIcon() : "");
                response.setVariables(variables);

                assignees.add(user != null ? user.getUserName() : active.getUserId());
                currentTasks.add(response);
            }
            approveRecord.setCurrentAssignees(String.join(",", assignees));
        }
        // 新类型 当前数据
        List<GTApproveRecordTask> newCurrentTasks = new ArrayList<>();
        if (!org.apache.commons.collections4.CollectionUtils.isEmpty(currentTasks)) {
            newCurrentTasks = newTasks(currentTasks);
        }

        // 处理转办记录
        List<HistoryTaskResponse> changeTasks = new ArrayList<>();
        // 新类型 转办数据
        List<GTApproveRecordTask> newChangeTasks = new ArrayList<>();
        List<ChangeAssigneeRecord> changeRecords = changeAssigneeRecordService.queryByProcessInstanceId(processInstanceId)
                .stream()
                .sorted(Comparator.comparing(ChangeAssigneeRecord::getOperateTime))
                .collect(Collectors.toList());

        if (!org.apache.commons.collections4.CollectionUtils.isEmpty(changeRecords)) {
            // 历史任务和当前任务合并，转办填充用
            List<HistoryTaskResponse> hisAndCurrentTasks = new ArrayList<>();
            hisAndCurrentTasks.addAll(historyTasks);
            hisAndCurrentTasks.addAll(currentTasks);
            // 获取相关用户信息
            Set<String> involvedUsers = changeRecords.stream()
                    .flatMap(r -> Stream.of(r.getOrignAssignee(), r.getNowAssignee()))
                    .collect(Collectors.toSet());
            CSysUser userQuery = new CSysUser();
            userQuery.setLoginCodes(involvedUsers.toArray(new String[0]));
            Map<String, SysUser> userMap = userApiSvc.getSysUserByCdt(userQuery).stream()
                    .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));

            // 按任务分组处理转办记录
            Map<String, List<ChangeAssigneeRecord>> taskChangeMap = changeRecords.stream()
                    .collect(Collectors.groupingBy(ChangeAssigneeRecord::getTaskId));

            for (HistoryTaskResponse task : hisAndCurrentTasks) {
                List<ChangeAssigneeRecord> changes = taskChangeMap.getOrDefault(task.getTaskId(), Collections.emptyList());
                for (ChangeAssigneeRecord change : changes) {
                    HistoryTaskResponse changeResponse = createChangeAssigneeResponse(change, userMap, task);
                    changeTasks.add(changeResponse);
                    newChangeTasks.add(createChangeAssigneeTask(changeResponse));
                }
            }
        }
        //组装数据
        oldTasks.addAll(historyTasks);
        oldTasks.addAll(currentTasks);
        oldTasks.addAll(changeTasks);
        newHistoryTask.addAll(newChangeTasks);
        //根据 commitTime 字段进行排序，从小到大
        newHistoryTask.sort(Comparator.comparing(GTApproveRecordTask::getCommitTime));
        newTasks.addAll(newHistoryTask);
        if (!org.apache.commons.collections4.CollectionUtils.isEmpty(newCurrentTasks)) {
            newTasks.addAll(newCurrentTasks);
        }
        approveRecord.setTasks(oldTasks);
        approveRecord.setNewTasks(newTasks);
        return approveRecord;
    }

    private HistoryTaskResponse createChangeAssigneeResponse(ChangeAssigneeRecord record,
                                                             Map<String, SysUser> userMap,
                                                             HistoryTaskResponse originalTask) {
        HistoryTaskResponse response = new HistoryTaskResponse();
        response.setTaskId(originalTask.getTaskId());
        response.setTaskName(originalTask.getTaskName());
        response.setUserId(record.getOrignAssignee());
        response.setCommitTime(parseOperateTime(record.getOperateTime()));
        response.setProcessInstanceName(originalTask.getProcessInstanceName());
        response.setDescription(originalTask.getDescription());

        Map<String, Object> vars = new ConcurrentHashMap<>();
        vars.put("pass", "change");
        SysUser fromUser = userMap.get(record.getOrignAssignee());
        SysUser toUser = userMap.get(record.getNowAssignee());
        vars.put("user", fromUser != null ? fromUser.getUserName() : record.getOrignAssignee());
        vars.put("icon", fromUser != null ? fromUser.getIcon() : "");
        vars.put("toUser", toUser != null ? toUser.getUserName() : record.getNowAssignee());
        vars.put("remarks", StringUtils.defaultString(record.getRemarks(), ""));
        response.setVariables(vars);

        return response;
    }

    private GTApproveRecordTask createChangeAssigneeTask(HistoryTaskResponse response) {
        GTApproveRecordTask task = new GTApproveRecordTask();
        task.setTaskIds(Collections.singletonList(response.getTaskId()));
        task.setTaskName(response.getTaskName());
        task.setCommitTime(response.getCommitTime());
        task.setUserIds(Collections.singletonList(response.getUserId()));
        task.setProcessInstanceName(response.getProcessInstanceName());
        task.setVariables(response.getVariables());
        task.setDescription(response.getDescription());
        task.setAssignees(Collections.singletonList(
                new GTApproveRecordTask.Assignee(
                        response.getVariables().get("user").toString(),
                        response.getTaskId()
                )
        ));
        return task;
    }

    private Date parseOperateTime(Long timeStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        LocalDateTime dateTime = LocalDateTime.parse(String.valueOf(timeStr), formatter);
        return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    private List<GTApproveRecordTask> newTasks(List<HistoryTaskResponse> tasks) {
        String stage = tasks.get(0).getDescription();
        List<List<HistoryTaskResponse>> groupStage = new ArrayList<>();
        List<HistoryTaskResponse> s = new ArrayList<>();
        for (HistoryTaskResponse task : tasks) {
            if (!stage.equals(task.getDescription())) {
                groupStage.add(EamUtil.copy(s, HistoryTaskResponse.class));
                stage = task.getDescription();
                s = new ArrayList<>();
            }
            s.add(task);
        }
        groupStage.add(EamUtil.copy(s, HistoryTaskResponse.class));

        List<GTApproveRecordTask> results = new ArrayList<>();
        for (List<HistoryTaskResponse> g : groupStage) {
            ArchReviewStage archReviewStage = gtFlowStage(g.get(0).getDescription());
            switch (archReviewStage) {
                case ORG_OR_SIGN:
                case PRE_REVIEW_OFFICER:
                case PRE_REVIEW_ROTA:
                case PMO_REVIEW:
                    initRecordsCaseOrSign(g, results);
                    break;
                case PRE_REVIEW_EXPERT:
                case PRE_REVIEW_FINAL_EXPERT:
                    initRecordsCaseMuiltyOrSign(g, results);
                    break;
                default:
                    for (HistoryTaskResponse response : g) {
                        GTApproveRecordTask approveRecordTask = new GTApproveRecordTask();
                        approveRecordTask.setTaskIds(Collections.singletonList(response.getTaskId()));
                        approveRecordTask.setTaskName(response.getTaskName());
                        approveRecordTask.setCommitTime(response.getCommitTime());
                        approveRecordTask.setUserIds(Collections.singletonList(response.getUserId()));
                        approveRecordTask.setProcessInstanceName(response.getProcessInstanceName());
                        approveRecordTask.setVariables(response.getVariables());
                        approveRecordTask.setDescription(response.getDescription());
                        Map<String, Object> variables = response.getVariables();
                        String user = response.getUserId();
                        if (!CollectionUtils.isEmpty(variables) && variables.get("user") != null) {
                            user = variables.get("user").toString();
                        }
                        String assigneeName = getAssigneeName(response, user);
                        approveRecordTask.setAssignees(Collections.singletonList(new GTApproveRecordTask.Assignee(assigneeName, response.getTaskId())));
                        results.add(approveRecordTask);
                    }
            }
        }
        return results;
    }

    private void initRecordsCaseMuiltyOrSign(List<HistoryTaskResponse> g, List<GTApproveRecordTask> results) {
        Map<String, List<HistoryTaskResponse>> groupByChildBizKey = new LinkedHashMap<>();
        for (HistoryTaskResponse response : g) {
            Map<String, Object> variables = response.getVariables();
            if (CollectionUtils.isEmpty(variables) || variables.get("childVariable") == null) {
                continue;
            }
            JSONObject json = JSON.parseObject(variables.get("childVariable").toString());
            if (StringUtils.isBlank(json.getString("childBusinessKey"))) {
                continue;
            }
            String key = json.getString("childBusinessKey");
            List<HistoryTaskResponse> list = groupByChildBizKey.getOrDefault(key, new ArrayList<>());
            list.add(response);
            groupByChildBizKey.put(key, list);
        }
        for (List<HistoryTaskResponse> group : groupByChildBizKey.values()) {
            initRecordsCaseOrSign(group, results);
        }
    }

    private void initRecordsCaseOrSign(List<HistoryTaskResponse> g, List<GTApproveRecordTask> results) {
        int toDoCount = 0;
        String opPass = "";
        Date opTime = null;
        String opRemark = "";
        List<String> opUsers = new ArrayList<>();

        String cancelRemark = "";
        List<String> cancelUsers = new ArrayList<>();

        List<String> todoUsers = new ArrayList<>();
        List<String> taskIds = new ArrayList<>();
        List<String> userIds = new ArrayList<>();
        List<GTApproveRecordTask.Assignee> assignees = new ArrayList<>();
        Set<String> options = Stream.of("pass", "noPass", "noAgree", "rejectInitOwner").collect(Collectors.toSet());
        for (HistoryTaskResponse response : g) {
            Map<String, Object> variables = response.getVariables();
            if (CollectionUtils.isEmpty(variables) || variables.get("pass") == null) {
                continue;
            }
            String p = variables.get("pass").toString();
            String user = getOpUser(variables, response);
            if (options.contains(p)) {
                opPass = p;
                opTime = response.getCommitTime();
                opRemark = variables.get("remarks") == null ? "" : variables.get("remarks").toString();
                opUsers.add(user);
            }
            if (p.equals("todo")) {
                toDoCount++;
                todoUsers.add(user);
                assignees.add(new GTApproveRecordTask.Assignee(getAssigneeName(response, user), response.getTaskId()));
            }
            if (p.equals("autoCLose") &&
                    variables.get("closeType") != null &&
                    "adminTerminal".equals(variables.get("closeType").toString())) {
                cancelUsers.add(user);
                cancelRemark = variables.get("remarks") == null ? "" : variables.get("remarks").toString();
            }
            taskIds.add(response.getTaskId());
            userIds.add(response.getUserId());

        }
        //待办
        if (toDoCount == g.size()) {
            GTApproveRecordTask approveRecordTask = new GTApproveRecordTask();
            approveRecordTask.setTaskIds(taskIds);
            approveRecordTask.setTaskName(g.get(0).getTaskName());
            approveRecordTask.setCommitTime(g.get(0).getCommitTime());
            approveRecordTask.setUserIds(userIds);
            approveRecordTask.setProcessInstanceName(g.get(0).getProcessInstanceName());
            Map<String, Object> variables = new HashMap<>();
            variables.put("pass", "todo");
            variables.put("user", String.join("，", todoUsers));
            variables.put("remarks", "");
            approveRecordTask.setVariables(variables);
            approveRecordTask.setDescription(g.get(0).getDescription());
            approveRecordTask.setAssignees(assignees);
            results.add(approveRecordTask);
            return;
        }
        //已办
        if (!CollectionUtils.isEmpty(opUsers)) {
            GTApproveRecordTask approveRecordTask = new GTApproveRecordTask();
            approveRecordTask.setTaskIds(taskIds);
            approveRecordTask.setTaskName(g.get(0).getTaskName());
            approveRecordTask.setCommitTime(opTime);
            approveRecordTask.setUserIds(userIds);
            approveRecordTask.setProcessInstanceName(g.get(0).getProcessInstanceName());
            Map<String, Object> variables = new HashMap<>();
            variables.put("pass", opPass);
            variables.put("user", String.join("，", opUsers));
            variables.put("remarks", opRemark);
            approveRecordTask.setVariables(variables);
            approveRecordTask.setDescription(g.get(0).getDescription());
            results.add(approveRecordTask);
            return;
        }
        //终止
        if (!CollectionUtils.isEmpty(cancelUsers)) {
            GTApproveRecordTask approveRecordTask = new GTApproveRecordTask();
            approveRecordTask.setTaskIds(taskIds);
            approveRecordTask.setTaskName(g.get(0).getTaskName());
            approveRecordTask.setCommitTime(g.get(0).getCommitTime());
            approveRecordTask.setUserIds(userIds);
            approveRecordTask.setProcessInstanceName(g.get(0).getProcessInstanceName());
            Map<String, Object> variables = new HashMap<>();
            variables.put("pass", "autoCLose");
            variables.put("user", String.join("，", cancelUsers));
            variables.put("remarks", cancelRemark);
            approveRecordTask.setVariables(variables);
            approveRecordTask.setDescription(g.get(0).getDescription());
            results.add(approveRecordTask);
        }
    }

    private String getOpUser(Map<String, Object> variables, HistoryTaskResponse response) {
        if (CollectionUtils.isEmpty(variables) || variables.get("user") == null) {
            return response.getUserId();
        }
        String user = variables.get("user").toString();
        return StringUtils.isBlank(user) ? response.getUserId() : user;
    }

    private String getAssigneeName(HistoryTaskResponse response, String user) {
        return String.format("%s（%s）", user, response.getTaskName());
    }

    private void extracted(List<HistoryTaskResponse> historyTaskByCurrentProcessInstances) {
        List<String> userList = historyTaskByCurrentProcessInstances.stream().map(HistoryTaskResponse::getUserId).collect(Collectors.toList());
        HashSet<String> userIdSet = new HashSet<>(userList);
        String[] userIds = new String[userIdSet.size()];
        CSysUser cSysUser = new CSysUser();
        userIdSet.toArray(userIds);
        cSysUser.setLoginCodes(userIds);
        List<SysUser> sysUserByCdt = userApiSvc.getSysUserByCdt(cSysUser);
        Map<String, SysUser> sysUserMap = sysUserByCdt.parallelStream()
                .collect(Collectors.toMap(SysUser::getLoginCode, u -> u));
        for (HistoryTaskResponse historyTaskByCurrentProcessInstance : historyTaskByCurrentProcessInstances) {
            String userId = historyTaskByCurrentProcessInstance.getUserId();
            Map<String, Object> variables = historyTaskByCurrentProcessInstance.getVariables();
            variables.put("user", sysUserMap.containsKey(userId) ? sysUserMap.get(userId).getUserName() : userId);
            variables.put("icon", sysUserMap.containsKey(userId) ? sysUserMap.get(userId).getIcon() : "");
        }
    }

    /**
     * 终止流程
     * @param processInstanceId
     */
    public void processTerm(String processInstanceId) {
        PorcessResponse porcessResponse = flowableFeign.getProcessInstanceByProcessInstanceId(processInstanceId);
        if (porcessResponse == null || !FlowableConstant.GT_TECHNICAL_SCHEME_APPROVE.equals(porcessResponse.getProcessDefinitionKey())) {
            throw new BusinessException("未找到架构评审流程");
        }
        //终结当前流程
        flowableFeign.terminateProcessInstanceById(processInstanceId, ADMIN_TERM_REMARKS);
        //更新架构评审状态
        ArchReviewInfo archReviewInfo = archReviewService.getArchReviewByProcessInstanceId(processInstanceId);
        if (archReviewInfo == null) {
            return;
        }
        archReviewService.updateArchReviewState(archReviewInfo.getId(), ArchReviewState.TERM);
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        //如果关联资产是项目 则更新项目的 架构评审状态 和 架构方案地址
        archReviewService.updateProject(archReviewInfo.getItemType(), archReviewInfo.getProjectCiCode(), plans, ArchReviewState.TERM, archReviewInfo.getProcessInstanceId(), String.valueOf(archReviewInfo.getCreateTime()), String.valueOf(ESUtil.getNumberDateTime()));
        //更新方案信息
        for (PlanDesignInstance plan : plans) {
            planDesignInstanceService.gtTermPlan(plan);
        }
        //所有待办关闭
        workbenchChargeDoneSvc.batchModifyWorkbenchTaskDone(processInstanceId, archReviewInfo.getId().toString());
        //推送终止消息
        String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        noticeService.workflowMsgSave("cancel", "archReview", archReviewInfo.getId().toString(),
                archReviewInfo.getCreatorCode(), curUserLoginCode, porcessResponse.getProcessInstanceName());
    }

    /**
     * 转办
     * @param request
     */
    public void changeAssignee(TaskRequest request) {
        TaskResponse taskResponse = flowableFeign.getTaskInfoByTaskId(request.getTaskId());
        Assert.notNull(taskResponse, "未找到当前任务");
        if (taskResponse.getTaskEndTime() != null) {
            throw new BusinessException("审批完成，不可操作转办");
        }
        //维护转办记录
        Long id = changeAssigneeRecordService.addRecord(request, taskResponse);
        //关闭原待办并维护新待办
        workbenchChargeDoneSvc.addChangeAssigness(request.getTaskId(), taskResponse.getUserId(), request.getUserId());
        try {
            flowableFeign.changeAssigness(request);
        } catch (Exception e) {
            log.error("转办失败：{}", JSON.toJSONString(e));
            changeAssigneeRecordService.deleteById(id);
            workbenchChargeDoneSvc.rollbackChangeAssigness(request.getTaskId(), taskResponse.getUserId(), request.getUserId());
        }
    }

    /**
     * 转办
     * @param request
     */
    public void changeAssigneeNew(TaskRequest request) {
        TaskResponse taskResponse = flowableFeign.getTaskInfoByTaskId(request.getTaskId());
        Assert.notNull(taskResponse, "未找到当前任务");
        if (taskResponse.getTaskEndTime() != null) {
            throw new BusinessException("审批完成，不可操作转办");
        }
        ArchReviewStage archReviewStage = gtFlowStageByTaskId(request.getTaskId());
        if (archReviewStage.equals(ArchReviewStage.ORG_OR_SIGN)
                || archReviewStage.equals(ArchReviewStage.PRE_REVIEW_ROTA)
                || archReviewStage.equals(ArchReviewStage.PMO_REVIEW)) {
            String processInstanceId = taskResponse.getProcessInstanceId();
            //查询当前未完成节点
            List<TaskResponse> currentTaskResponseList = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
            orSignChangeAssignee(currentTaskResponseList, request, taskResponse);
        }else if(archReviewStage.equals(ArchReviewStage.PRE_REVIEW_EXPERT)){
            //如果 是预评审-专家初审节点
            List<TaskResponse> currentTaskResponseList = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(taskResponse.getProcessInstanceId());
            // 按照领域分组
            Map<String, List<TaskResponse>> groupByChildBizKey = new LinkedHashMap<>();
            for (TaskResponse response : currentTaskResponseList) {
                Map<String, Object> variables = response.getTaskEchoVariables();
                if (CollectionUtils.isEmpty(variables) || variables.get("childVariable") == null) {
                    continue;
                }
                JSONObject json = JSON.parseObject(variables.get("childVariable").toString());
                if (StringUtils.isBlank(json.getString("childBusinessKey"))) {
                    continue;
                }
                String key = json.getString("childBusinessKey");
                List<TaskResponse> list = groupByChildBizKey.getOrDefault(key, new ArrayList<>());
                list.add(response);
                groupByChildBizKey.put(key, list);
            }
            // 判断当前 task 在哪个分组下
            List<TaskResponse> groupList = new ArrayList<>();
            String currentTaskId = taskResponse.getTaskId();
            for (Map.Entry<String, List<TaskResponse>> entry : groupByChildBizKey.entrySet()) {
                for (TaskResponse task : entry.getValue()) {
                    if (currentTaskId.equals(task.getTaskId())) {
                        groupList = entry.getValue();
                        break;
                    }
                }
            }
            if (CollectionUtils.isEmpty(groupList)) {
                throw new BusinessException("未找到当前任务");
            }
            orSignChangeAssignee(groupList, request, taskResponse);
        }
        // 会签节点，不处理其他任务
        else {
            signChangAssignee(request, taskResponse);
        }
    }

    /**
     * 会签节点转办处理
     */
    private void signChangAssignee(TaskRequest request,TaskResponse taskResponse){
        //维护转办记录
        Long id = changeAssigneeRecordService.addRecord(request, taskResponse);
        //关闭原待办并维护新待办
        workbenchChargeDoneSvc.addChangeAssigness(request.getTaskId(), taskResponse.getUserId(), request.getUserId());
        try {
            flowableFeign.changeAssigness(request);
        } catch (Exception e) {
            log.error("转办失败：{}", JSON.toJSONString(e));
            changeAssigneeRecordService.deleteById(id);
            workbenchChargeDoneSvc.rollbackChangeAssigness(request.getTaskId(), taskResponse.getUserId(), request.getUserId());
        }
    }

    /**
     * 或签节点转办处理
     *
     * @param request
     * @param taskResponse
     * @return
     */
    private void orSignChangeAssignee(List<TaskResponse> currentTaskResponseList,TaskRequest request,TaskResponse taskResponse) {
        Map<String, TaskResponse> taskResponseMap = currentTaskResponseList.stream().collect(Collectors.toMap(c -> c.getTaskId(), c -> c));
        Set<String> collect = currentTaskResponseList.stream().map(c -> c.getTaskId()).collect(Collectors.toSet());
        String userId = request.getUserId();
        String taskId = request.getTaskId();
        //维护转办记录
        Long id = changeAssigneeRecordService.addRecordByUserId(userId, taskResponse, request.getRemarks());
        //关闭原待办并维护新待办
        workbenchChargeDoneSvc.closeChangeAssigness(collect, taskResponse.getUserId(), userId);
        try {
            // 转办，修改流程负责人
            TaskRequest taskRequest = new TaskRequest();
            taskRequest.setTaskId(taskId);
            taskRequest.setUserId(userId);
            flowableFeign.changeAssigness(taskRequest);
            // 其他任务进行已办操作 挂起
            Iterator<Map.Entry<String, TaskResponse>> iterator = taskResponseMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, TaskResponse> next = iterator.next();
                if (!next.getKey().equals(taskId)) {
                    TaskResponse value = next.getValue();
                    flowableFeign.suspendedTask(value.getTaskId());
                }
            }
        } catch (Exception e) {
            log.error("转办失败：{}", JSON.toJSONString(e));
            changeAssigneeRecordService.deleteById(id);
            workbenchChargeDoneSvc.rollbackChangeAssignessAll(taskId, collect, taskResponse.getUserId(), userId);
        }
    }

    public ArchReviewSubmitRes submitApprove(ArchReviewReSubmitVo vo, TaskResponse task) {
        //退回至申请人，维护相关信息
        ArchReviewStage stage = gtFlowStage(task.getDescription());
        if (!rejectToSubmitterNodes().contains(stage)) {
            throw new BinaryException("非[退回至申请人]节点，不可提交评审");
        }
        Long archReviewInfoId = Long.valueOf(task.getBusinessKey());
        ArchReviewInfo archReviewInfo = archReviewService.getById(archReviewInfoId);
        Assert.notNull(archReviewInfo, "未找到架构评审信息");
        //方案发布校验
        List<SimpPlanInfo> checkUnPassPlans = new ArrayList<>();
        for (SimpPlanInfo planInfo : archReviewInfo.getPlanInfos()) {
            Map<String,Object> checkResult = planDesignInstanceService.checkPlan(planInfo.getId());
            if (Boolean.FALSE.equals(checkResult.get("result"))
                    || Boolean.FALSE.equals(checkResult.get("assertDirExist"))) {
                checkUnPassPlans.add(planInfo);
            }
        }
        ArchReviewSubmitRes res = new ArchReviewSubmitRes(CollectionUtils.isEmpty(checkUnPassPlans), checkUnPassPlans);
        if (!res.getResult()) {
            return res;
        }
        archReviewInfo.setRefLink(vo.getRefLink());
        archReviewInfo.setReviewDesc(vo.getReviewDesc());
        //(1)驳回后，申请人可修改如下信息：
        // 预评审专家评审节点前驳回，可修改1-4项信息；
        // 预评审专家评审节点开始驳回时，不可修改1-4项信息；
        // 若PMO审核时，选择“驳回至初始节点”，也可以修改1-4项信息：
        //    ①	是否涉及架构变更
        //    ②	变更级别
        //    ③	是否定制化系统
        //    ④	是否涉及重大逻辑变更
        //    ⑤	架构方案
        if (ArchReviewStage.START_USER_SUBMIT.equals(stage)
                || ArchReviewStage.ORG_COUNTER_SIGN_REJECT.equals(stage)
                || ArchReviewStage.ORG_OR_SIGN_REJECT.equals(stage)
                || ArchReviewStage.PRE_REVIEW_OFFICER_REJECT.equals(stage)
                || ArchReviewStage.PRE_REVIEW_ROTA_REJECT.equals(stage)
                || ArchReviewStage.PMO_REVIEW_REJCET.equals(stage)) {
            if (vo.getCustomizeSys() != null) {
                archReviewInfo.setCustomizeSys(vo.getCustomizeSys());
            }
            if (vo.getIncludeBizTrans() != null) {
                archReviewInfo.setIncludeBizTrans(vo.getIncludeBizTrans());
            }
            if (vo.getIncludeArchTrans() != null) {
                archReviewInfo.setIncludeArchTrans(vo.getIncludeArchTrans());
            }
            if (StringUtils.isNotBlank(vo.getTransLevel())) {
                archReviewInfo.setTransLevel(TransLevel.valueOf(vo.getTransLevel()));
            }
            archReviewService.saveOrUpdate(archReviewInfo);
        }
        TaskRequest taskRequest = new TaskRequest();
        taskRequest.setTaskId(vo.getTaskId());
        taskRequest.setAction(FLOWACTION.ACCETP);
        taskRequest.setRemarks(vo.getRemarks());

        //任务本地变量维护流程批次（驳回至初始节点算新的批次）,方便查任务时返回对应的度量记录
        setFlowBatchInTaskInfo(archReviewInfoId, taskRequest);

        //被PMO驳回到初始节点，申请人提交时需要重新判断流程链路
        if (ArchReviewStage.START_USER_SUBMIT.equals(stage)) {
            submitApproveCaseStartUserSubmitStage(taskRequest, archReviewInfo);
        } else {
            flowableFeign.completeTask(taskRequest);
        }
        //修改方案状态为审批中
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        for (PlanDesignInstance plan : plans) {
            plan.setStatus(PlanStatusEnum.approve.name());
        }
        planDesignInstanceService.saveOrUpdateBatch(plans);
        return res;
    }

    private void setFlowBatchInTaskInfo(Long archReviewInfoId, TaskRequest taskRequest) {
        List<MeasureFlowRecord> measureFlowRecords = measureFlowRecordService.queryByArchReviewId(archReviewInfoId);
        Integer flowBatch = CollectionUtils.isEmpty(measureFlowRecords) ? 1 : measureFlowRecords.get(0).getFlowBatch();
        Map<String, Object> taskEchoVariables = new HashMap<>();
        taskEchoVariables.put("flowBatch", flowBatch);
        taskRequest.setTaskEchoVariables(taskEchoVariables);
    }

    private void submitApproveCaseStartUserSubmitStage(TaskRequest taskRequest, ArchReviewInfo reviewInfo) {
        //查团队架构评审配置
        Long belongOrgId = reviewInfo.getBelongOrgId();
        ArchReviewOrgDetailConf orgDetailConf = orgConfService.getLatestByOrgId(belongOrgId);
        log.info("查团队架构评审配置:orgId:{}, orgDetailConf:{}", belongOrgId, JSON.toJSONString(orgDetailConf));
        //无团队评审
        if (orgDetailConf == null) {
            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("orgApprove", "jump");
            taskRequest.setRouterVariables(stringObjectHashMap);
            flowableFeign.completeTask(taskRequest);
            return;
        }
        //或签
        if (ArchReviewOrgRule.OR_SIGN.equals(orgDetailConf.getRule())) {
            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("orgApprove", "orSign");
            stringObjectHashMap.put("assigneeList", orgDetailConf.getPersions());
            taskRequest.setRouterVariables(stringObjectHashMap);
            flowableFeign.completeTask(taskRequest);
            return;
        }
        //会签
        List<ChildProcessRequest> childProcessRequests = new ArrayList<>();
        //会签开启子流程审明一个id，用于会签子流程驳回时消息不重发
        Long countersignId = ESUtil.getUUID();
        for (String persion : orgDetailConf.getPersions()) {
            ChildProcessRequest childProcessRequest = new ChildProcessRequest();
            childProcessRequest.setChildBusinessKey(reviewInfo.getId() + "_CHILD_ORG_SUB_" + persion);
            Map<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("assignee", persion);
            stringObjectHashMap.put("countersignId", countersignId);
            childProcessRequest.setChildVariable(stringObjectHashMap);
            childProcessRequests.add(childProcessRequest);
        }
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("orgApprove", "countersign");
        stringObjectHashMap.put("activeChildProcess", "true");
        taskRequest.setRouterVariables(stringObjectHashMap);
        taskRequest.setChildProcessRequestList(childProcessRequests);
        flowableFeign.completeTask(taskRequest);
    }

    public Boolean selectDataStatus(String businessKey) {
        Long archReviewInfoId = Long.valueOf(businessKey);
        ArchReviewInfo archReviewInfo = archReviewService.getById(archReviewInfoId);
        Assert.notNull(archReviewInfo, "未找到架构评审信息");
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewInfo.getPlanIds());
        if (CollectionUtils.isEmpty(plans) || plans.size() != archReviewInfo.getPlanIds().size()) {
            return Boolean.TRUE;
        }
        for (PlanDesignInstance plan : plans) {
            if (plan.getStatus().equals("deleted")) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    public List<CurrentUserRecordVO> getCurrentUserRecords(String processInstanceId) {
        List<TaskResponse> actives = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
        List<CurrentUserRecordVO> result = new ArrayList<>();
        List<String> taskList = actives.stream().map(item -> item.getTaskId()).collect(Collectors.toList());
        Map<String,Long> workbenchMap = getWorkbenchByTaskId(taskList);
        for (TaskResponse active : actives) {
            String taskUserCode = active.getUserId();
            if (!SysUtil.getCurrentUserInfo().getLoginCode().equals(taskUserCode)) {
                continue;
            }
            CurrentUserRecordVO currentUserRecordVO = new CurrentUserRecordVO();
            currentUserRecordVO.setTaskId(active.getTaskId());
            currentUserRecordVO.setTaskDefinitionKey(active.getTaskDefinitionKey());
            currentUserRecordVO.setTaskName(active.getTaskName());
            currentUserRecordVO.setBusinessKey(active.getBusinessKey());
            currentUserRecordVO.setCurrentUserCode(active.getUserId());
            currentUserRecordVO.setProcessInstanceId(active.getProcessInstanceId());
            currentUserRecordVO.setProcessInstanceName(active.getProcessInstanceName());
            currentUserRecordVO.setId(workbenchMap.get(active.getTaskId()));
            result.add(currentUserRecordVO);
        }
        return result;
    }

    private Map<String, Long> getWorkbenchByTaskId(List<String> taskList) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termsQuery("taskId.keyword",taskList));
        boolQueryBuilder.must(QueryBuilders.termQuery("action",1));
        boolQueryBuilder.must(QueryBuilders.termQuery("userId.keyword",SysUtil.getCurrentUserInfo().getLoginCode()));
        List<WorkbenchChargeDone> workbenchList = workbenchChargeDoneDao.getListByQuery(boolQueryBuilder);
        return workbenchList.stream().collect(Collectors.toMap(item -> item.getTaskId(),item -> item.getId(),(k1,k2)->k1));
    }

    public void changeAssignessForDr(AssignessInfo assignessInfo) {
        if (BinaryUtils.isEmpty(assignessInfo.getProcessInstanceId())) {
            throw new BusinessException("流程实例ID不能为空");
        }
        if (BinaryUtils.isEmpty(assignessInfo.getTaskId())) {
            throw new BusinessException("任务ID不能为空");
        }
        if (BinaryUtils.isEmpty(assignessInfo.getUserId())) {
            throw  new BusinessException("用户ID不能为空");
        }
        String processInstanceId = assignessInfo.getProcessInstanceId();
        String taskId = assignessInfo.getTaskId();
        String userId = assignessInfo.getUserId();
        //查询当前未完成节点
        List<TaskResponse> currentTaskResponseList = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
        // 根据getTaskId分组
        Map<String, TaskResponse> taskResponseMap = currentTaskResponseList.stream().collect(Collectors.toMap(c -> c.getTaskId(), c -> c));
        if (!taskResponseMap.containsKey(taskId)) {
            throw new BusinessException("未找到当前任务");
        }
        TaskResponse taskResponse = taskResponseMap.get(taskId);

        Set<String> collect = currentTaskResponseList.stream().map(c -> c.getTaskId()).collect(Collectors.toSet());
        //维护转办记录
        Long id = changeAssigneeRecordService.addRecordByUserId(userId, taskResponse, assignessInfo.getRemarks());
        //关闭原待办并维护新待办
        workbenchChargeDoneSvc.closeChangeAssigness(collect, taskResponse.getUserId(), userId);
        try {
            // 转办，修改流程负责人
            TaskRequest taskRequest = new TaskRequest();
            taskRequest.setTaskId(taskId);
            taskRequest.setUserId(userId);
            flowableFeign.changeAssigness(taskRequest);
            // 其他任务进行已办操作
            Iterator<Map.Entry<String, TaskResponse>> iterator = taskResponseMap.entrySet().iterator();
            while (iterator.hasNext()){
                Map.Entry<String, TaskResponse> next = iterator.next();
                if (!next.getKey().equals(taskId)) {
                    TaskResponse value = next.getValue();
                    flowableFeign.suspendedTask(value.getTaskId());
//                    TaskRequest request = new TaskRequest();
//                    request.setTaskId(value.getTaskId());
//                    request.setUserId(null);
//                    flowableFeign.changeAssigness(request);
//                    flowableFeign.resolveTask(value.getTaskId());
                }
            }
        } catch (Exception e) {
            log.error("转办失败：{}", JSON.toJSONString(e));
            changeAssigneeRecordService.deleteById(id);
            workbenchChargeDoneSvc.rollbackChangeAssignessAll(taskId,collect, taskResponse.getUserId(), userId);
        }
    }


    public void communicateForDr(AssignessInfo assignessInfo) {
        if (BinaryUtils.isEmpty(assignessInfo.getProcessInstanceId())) {
            throw new BusinessException("流程实例ID不能为空");
        }
        if (BinaryUtils.isEmpty(assignessInfo.getTaskId())) {
            throw new BusinessException("任务ID不能为空");
        }
        if (BinaryUtils.isEmpty(assignessInfo.getUserId())) {
            throw  new BusinessException("用户ID不能为空");
        }
        String processInstanceId = assignessInfo.getProcessInstanceId();
        String taskId = assignessInfo.getTaskId();
        String userId = assignessInfo.getUserId();
        //查询当前未完成节点
        List<TaskResponse> currentTaskResponseList = flowableFeign.getCurrentTaskByCurrentProcessInstanceId(processInstanceId);
        // 根据getTaskId分组
        Map<String, TaskResponse> taskResponseMap = currentTaskResponseList.stream().collect(Collectors.toMap(c -> c.getTaskId(), c -> c));
        if (!taskResponseMap.containsKey(taskId)) {
            throw new BusinessException("未找到当前任务");
        }
        TaskResponse taskResponse = taskResponseMap.get(taskId);

        Set<String> collect = currentTaskResponseList.stream().map(c -> c.getTaskId()).collect(Collectors.toSet());
        //维护沟通记录
        long id = communicateRecordService.addRecord(assignessInfo, taskResponse, false);
        //关闭原待办并维护新的沟通代办
        workbenchChargeDoneSvc.closeAndAddCommunicate(collect, taskResponse.getUserId(), userId,id);
        try {
            // 沟通与转办一致，修改任务负责人，其他或签流程挂起
            TaskRequest taskRequest = new TaskRequest();
            taskRequest.setTaskId(taskId);
            taskRequest.setUserId(userId);
            flowableFeign.changeAssigness(taskRequest);
            // 其他任务进行已办操作
            Iterator<Map.Entry<String, TaskResponse>> iterator = taskResponseMap.entrySet().iterator();
            while (iterator.hasNext()){
                Map.Entry<String, TaskResponse> next = iterator.next();
                if (!next.getKey().equals(taskId)) {
                    TaskResponse value = next.getValue();
                    flowableFeign.suspendedTask(value.getTaskId());
                }
            }
        } catch (Exception e) {
            log.error("沟通失败：{}", JSON.toJSONString(e));
            communicateRecordService.deleteById(id);
            workbenchChargeDoneSvc.rollbackChangeAssignessAll(taskId,collect, taskResponse.getUserId(), userId);
        }
    }

    public void communicateCommit(AssignessInfo assignessInfo) {
        if (BinaryUtils.isEmpty(assignessInfo.getProcessInstanceId())) {
            throw new BusinessException("流程实例ID不能为空");
        }
        if (BinaryUtils.isEmpty(assignessInfo.getTaskId())) {
            throw new BusinessException("任务ID不能为空");
        }
        if (BinaryUtils.isEmpty(assignessInfo.getCommunicateId())) {
            throw new BusinessException("沟通记录id不能为空");
        }
        // 查询沟通记录
        CommunicateRecord byId = communicateRecordService.getById(assignessInfo.getCommunicateId());
        Assert.notNull(byId, "未找到当前沟通记录");
        // 将任务给沟通发起人
        assignessInfo.setUserId(byId.getCommunicatePromoter());

        TaskResponse taskResponse = flowableFeign.getTaskInfoByTaskId(assignessInfo.getTaskId());
        Assert.notNull(taskResponse, "未找到当前任务");
        if (taskResponse.getTaskEndTime() != null) {
            throw new BusinessException("审批完成，不可提交沟通");
        }
        // 维护沟通记录
        long id = communicateRecordService.addRecord(assignessInfo, taskResponse, true);
        // 关闭原代办维护新的沟通代办
        workbenchChargeDoneSvc.addCommunicate(assignessInfo.getTaskId(), taskResponse.getUserId(), assignessInfo.getUserId());
        try {
            // 沟通与转办逻辑一致 替换任务负责人
            TaskRequest taskRequest = new TaskRequest();
            taskRequest.setTaskId(assignessInfo.getTaskId());
            taskRequest.setUserId(assignessInfo.getUserId());
            flowableFeign.changeAssigness(taskRequest);
        } catch (Exception e) {
            log.error("沟通失败：{}", JSON.toJSONString(e));
            communicateRecordService.deleteById(id);
            workbenchChargeDoneSvc.rollbackChangeAssigness(assignessInfo.getTaskId(), taskResponse.getUserId(), assignessInfo.getUserId());
        }
    }
}
